package com.lframework.xingyun.basedata.impl.product;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.lframework.starter.common.exceptions.impl.DefaultClientException;
import com.lframework.starter.web.core.impl.BaseMpServiceImpl;
import com.lframework.starter.web.core.utils.IdUtil;
import com.lframework.xingyun.basedata.dto.product.ProductBundleDto;
import com.lframework.xingyun.basedata.dto.product.ProductPropertyRelationDto;
import com.lframework.xingyun.basedata.entity.*;
import com.lframework.xingyun.basedata.enums.ProductType;
import com.lframework.xingyun.basedata.mappers.ProductMapper;
import com.lframework.xingyun.basedata.service.product.*;
import com.lframework.xingyun.basedata.service.shop.ShopService;
import com.lframework.xingyun.basedata.vo.product.shop.SyncProductShopVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

@Slf4j
@Service
public class ProductShopServiceImpl extends BaseMpServiceImpl<ProductMapper, Product> implements ProductShopService {

    @Autowired
    private ShopService shopService;

    @Autowired
    private ProductService productService;

    @Autowired
    private ProductPurchaseService productPurchaseService;

    @Autowired
    private ProductSaleService productSaleService;

    @Autowired
    private ProductRetailService productRetailService;

    @Autowired
    private ProductPropertyRelationService productPropertyRelationService;

    @Autowired
    private ProductBundleService productBundleService;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void syncProductShop(SyncProductShopVo vo) {
        // 原商品
        Product syncProduct = lambdaQuery().eq(Product::getId, vo.getProductId()).one();
        if (Objects.isNull(syncProduct)) {
            throw new DefaultClientException("选择商品有误, 请重新选择!");
        }
        // 同步门店
        boolean exists = shopService.lambdaQuery().eq(Shop::getId, vo.getShopId()).exists();
        if (!exists) {
            throw new DefaultClientException("选择门店有误, 请重新选择!");
        }

        ProductType productType = syncProduct.getProductType();
        if (ProductType.NORMAL == productType) {
            syncNormalProduct(syncProduct, vo, true);
        }
        else if (ProductType.BUNDLE == productType) {
            syncBundleProduct(syncProduct, vo);
        }
        else if (ProductType.SPEC == productType) {
            syncSpecProduct(syncProduct, vo);
        }
    }

    /**
     * 同步单品
     * @param syncProduct
     * @param vo
     */
    private Optional<Product> syncNormalProduct(Product syncProduct, SyncProductShopVo vo, Boolean mainProductIdFlag) {
        Optional<Product> normalProduct = syncProduct(syncProduct, vo, mainProductIdFlag);
        normalProduct.ifPresent(product -> {
            syncPurchasePrice(vo.getPurchasePrice(), syncProduct.getId(), product.getId());
            syncSalePrice(vo.getSalePrice(), syncProduct.getId(), product.getId());
            syncRetailPrice(vo.getRetailPrice(), syncProduct.getId(), product.getId());
            syncProperties(vo.getProperties(), syncProduct.getId(), product.getId());
        });
        return normalProduct;
    }

    /**
     * 同步组合商品
     * @param syncProduct
     * @param vo
     */
    private void syncBundleProduct(Product syncProduct, SyncProductShopVo vo) {
        // 主商品
        syncNormalProduct(syncProduct, vo, true).ifPresent(mainProduct -> {
            productBundleService.lambdaUpdate().eq(ProductBundle::getMainProductId, mainProduct.getId()).remove();
            productBundleService.cleanCacheByKey(mainProduct.getId());
            List<ProductBundleDto> productBundleDtos = productBundleService.getDtoByProductId(syncProduct.getId());
            if (CollUtil.isNotEmpty(productBundleDtos)) {
                List<ProductBundle> saveProductBundles = new ArrayList<>();
                for (ProductBundleDto productBundleDto : productBundleDtos) {
                    // 组合商品中关联单品
                    syncNormalProduct(productBundleDto.getProduct(), vo, false).ifPresent(normalProduct -> {
                        ProductBundle productBundle = BeanUtil.copyProperties(productBundleDto, ProductBundle.class,
                                "id", "mainProductId", "productId",
                                "createById", "createBy", "createTime", "updateBy", "updateById", "updateTime");
                        productBundle.setMainProductId(mainProduct.getId());
                        productBundle.setProductId(normalProduct.getId());
                        saveProductBundles.add(productBundle);
                    });
                }
                if (CollUtil.isNotEmpty(saveProductBundles)) {
                    productBundleService.saveBatch(saveProductBundles);
                }
            }
        });
    }

    /**
     * 同步规格商品
     * @param syncProduct
     * @param vo
     */
    private void syncSpecProduct(Product syncProduct, SyncProductShopVo vo) {
        // 主商品
        syncNormalProduct(syncProduct, vo, true).ifPresent(mainProduct -> {
            List<Product> specProducts = lambdaQuery().eq(Product::getMainProductId, syncProduct.getId()).list();
            if (CollUtil.isNotEmpty(specProducts)) {
                for (Product specProduct : specProducts) {
                    specProduct.setMainProductId(mainProduct.getId());
                    syncNormalProduct(specProduct, vo, true);
                }
            }
        });
    }

    /**
     * 同步商品信息
     * @param syncProduct
     * @param vo
     * @return
     */
    private Optional<Product> syncProduct(Product syncProduct, SyncProductShopVo vo, Boolean mainProductIdFlag) {
        boolean updateRes = lambdaUpdate()
                .eq(Product::getCode, syncProduct.getCode())
                .eq(Product::getSkuCode, syncProduct.getSkuCode())
                .eq(Product::getShopId, vo.getShopId())
                .set(Product::getName, syncProduct.getName())
                .set(Product::getShortName, syncProduct.getShortName())
                .set(Product::getExternalCode, syncProduct.getExternalCode())
                .set(Product::getCategoryId, syncProduct.getCategoryId())
                .set(Product::getBrandId, syncProduct.getBrandId())
                .set(Product::getProductType, syncProduct.getProductType())
                .set(Product::getTaxRate, syncProduct.getTaxRate())
                .set(Product::getSaleTaxRate, syncProduct.getSaleTaxRate())
                .set(Product::getSpec, syncProduct.getSpec())
                .set(Product::getUnit, syncProduct.getUnit())
                .set(Product::getImageUrl, syncProduct.getImageUrl())
                .set(Product::getWeight, syncProduct.getWeight())
                .set(Product::getVolume, syncProduct.getVolume())
                .set(Product::getExpiryDay, syncProduct.getExpiryDay())
                .set(Product::getIntroduction, syncProduct.getIntroduction())
                .set(mainProductIdFlag, Product::getMainProductId, syncProduct.getMainProductId())
                .set(Product::getValidPropertyValues, syncProduct.getValidPropertyValues())
                .set(vo.getAvailable(), Product::getAvailable, syncProduct.getAvailable())
                .set(Product::getUpdateTime, DateUtil.date().toLocalDateTime())
                .update();
        if (!updateRes) {
            String mainProductIdProperties = mainProductIdFlag ? "id" : "mainProductId";
            Product newProduct = BeanUtil.copyProperties(syncProduct, Product.class,
                    "id", "createById", "createBy", "createTime", "updateBy", "updateById", "updateTime", mainProductIdProperties);
            newProduct.setShopId(vo.getShopId());
            save(newProduct);
        }
        return lambdaQuery().eq(Product::getCode, syncProduct.getCode())
                .eq(Product::getSkuCode, syncProduct.getSkuCode())
                .eq(Product::getShopId, vo.getShopId()).oneOpt().filter(product -> {
                    productService.cleanCacheByKey(product.getId());
                    return true;
                });
    }

    /**
     * 同步商品采购价
     * @param purchasePriceFlag
     * @param syncProductId
     * @param productId
     */
    private void syncPurchasePrice(Boolean purchasePriceFlag, String syncProductId, String productId) {
        if (purchasePriceFlag) {
            productPurchaseService.lambdaQuery().eq(ProductPurchase::getId, syncProductId).oneOpt()
                .ifPresent(purchasePrice -> {
                    boolean updateRes = productPurchaseService.lambdaUpdate()
                            .eq(ProductPurchase::getId, productId)
                            .set(ProductPurchase::getPrice, purchasePrice.getPrice())
                            .update();
                    if (!updateRes) {
                        ProductPurchase productPurchase = BeanUtil.copyProperties(purchasePrice, ProductPurchase.class, "id");
                        productPurchase.setId(productId);
                        productPurchaseService.save(productPurchase);
                    }
                });
        }
    }

    /**
     * 同步商品销售价
     * @param salePriceFlag
     * @param syncProductId
     * @param productId
     */
    private void syncSalePrice(Boolean salePriceFlag, String syncProductId, String productId) {
        if (salePriceFlag) {
            productSaleService.lambdaQuery().eq(ProductSale::getId, syncProductId).oneOpt()
                .ifPresent(salePrice -> {
                    boolean updateRes = productSaleService.lambdaUpdate()
                            .eq(ProductSale::getId, productId)
                            .set(ProductSale::getPrice, salePrice.getPrice())
                            .update();
                    if (!updateRes) {
                        ProductSale productSale = BeanUtil.copyProperties(salePrice, ProductSale.class, "id");
                        productSale.setId(productId);
                        productSaleService.save(productSale);
                    }
                });
        }
    }

    /**
     * 同步商品零售价
     * @param retailPriceFlag
     * @param syncProductId
     * @param productId
     */
    private void syncRetailPrice(Boolean retailPriceFlag, String syncProductId, String productId) {
        if (retailPriceFlag) {
            productRetailService.lambdaQuery().eq(ProductRetail::getId, syncProductId).oneOpt()
                .ifPresent(retailPrice -> {
                    boolean updateRes = productRetailService.lambdaUpdate()
                            .eq(ProductRetail::getId, productId)
                            .set(ProductRetail::getPrice, retailPrice.getPrice())
                            .update();
                    if (!updateRes) {
                        ProductRetail productRetail = BeanUtil.copyProperties(retailPrice, ProductRetail.class, "id");
                        productRetail.setId(productId);
                        productRetailService.save(productRetail);
                    }
                });
        }
    }

    /**
     * 同步商品属性
     * @param propertiesFlag
     * @param syncProductId
     * @param productId
     */
    private void syncProperties(Boolean propertiesFlag, String syncProductId, String productId) {
        if (propertiesFlag) {
            // 删除原有属性
            productPropertyRelationService.lambdaUpdate().eq(ProductPropertyRelation::getProductId, productId).remove();
            productPropertyRelationService.cleanCacheByKey(productId);
            List<ProductPropertyRelation> productPropertyRelations = productPropertyRelationService.lambdaQuery()
                    .eq(ProductPropertyRelation::getProductId, syncProductId)
                    .list();
            if (CollUtil.isNotEmpty(productPropertyRelations)) {
                for (ProductPropertyRelation productPropertyRelation : productPropertyRelations) {
                    productPropertyRelation.setId(IdUtil.getId());
                    productPropertyRelation.setProductId(productId);
                }
                productPropertyRelationService.saveBatch(productPropertyRelations);
            }
        }
    }
}
